package com.ler.yrmall.product.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ler.yrmall.search.api.SearchApi;
import com.ler.yrmall.search.dto.SkuEsModelDTO;
import com.ler.yrmall.basic.Result;
import com.ler.yrmall.enums.ErrorCodeEnum;
import com.ler.yrmall.exception.BusinessException;
import com.ler.yrmall.product.dto.SpuDTO;
import com.ler.yrmall.product.entity.*;
import com.ler.yrmall.product.form.SpuForm;
import com.ler.yrmall.product.mapper.SpuInfoMapper;
import com.ler.yrmall.product.service.*;
import com.ler.yrmall.product.vo.*;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author leron
 */
@Service
public class SpuServiceImpl extends ServiceImpl<SpuInfoMapper, SpuEntity> implements SpuService {


    @Resource
    private SpuDetailService spuDetailService;

    @Resource
    private SpuImagesService spuImagesService;

    @Resource
    private AttrService attrService;

    @Resource
    private SpuAttrValueService spuAttrValueService;

    @Resource
    private SkuService skuService;

    @Resource
    private SkuAttrValueService skuAttrValueService;

    @Resource
    private CategoryService categoryService;

    @Resource
    private BrandService brandService;

    @DubboReference
    private SearchApi searchApi;

    @Resource
    private AttrGroupService attrGroupService;

    @Resource
    private ThreadPoolExecutor executor;


    @Override
    public IPage<SpuVO> find(SpuForm request) {
        Page<Object> page = request.getPage();
        return baseMapper.find(page,request);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSpu(SpuForm request) {
        // 基本信息
        SpuEntity spu = new SpuEntity();
        spu.setMainImg(request.getImages().get(0));
        BeanUtils.copyProperties(request,spu);
        this.save(spu);

        // 详细信息
        SpuDetailEntity spuDetail = new SpuDetailEntity();
        spuDetail.setId(spu.getId());
        spuDetail.setDetail(request.getDetail());
        spuDetailService.save(spuDetail);

        // 图片信息
        List<String> images = request.getImages();
        spuImagesService.saveImages(spu.getId(),images);

        // 规格信息
        spuAttrValueService.saveBatchSpuBaseAttrs(request.getBaseAttrs(),spu.getId());

        // sku 信息
        this.saveSkuAndSkuAttr(request.getSkus(), spu);
    }

    private void saveSkuAndSkuAttr(List<SpuForm.Sku> skus, SpuEntity spu) {
        skuService.deleteBySpuId(spu.getId());
        skus.forEach(item -> {
            SkuEntity skuInfoEntity = new SkuEntity();
            BeanUtils.copyProperties(item,skuInfoEntity);
            skuInfoEntity.setBrandId(spu.getBrandId());
            skuInfoEntity.setCategoryId(spu.getCategoryId());
            skuInfoEntity.setSaleCount(0L);
            skuInfoEntity.setSpuId(spu.getId());
            skuInfoEntity.setCover(item.getCover());
            skuService.save(skuInfoEntity);
            Long skuId = skuInfoEntity.getId();

            List<SpuForm.Attr> attr = item.getAttrs();
            List<SkuAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(e -> {
                SkuAttrValueEntity entity = new SkuAttrValueEntity();
                BeanUtils.copyProperties(e, entity);
                entity.setId(null);
                entity.setAttrId(e.getId());
                entity.setSkuId(skuId);
                return entity;
            }).collect(Collectors.toList());
            skuAttrValueService.saveBatch(skuSaleAttrValueEntities);
        });
    }

    @Override
    public void updateSpu(SpuForm request) {

        // 基础信息
        Assert.notNull(request.getId(),() -> new RuntimeException("参数缺失"));
        SpuEntity spuInfoEntity = this.getById(request.getId());
        spuInfoEntity.setMainImg(request.getImages().get(0));
        BeanUtils.copyProperties(request,spuInfoEntity);
        this.updateById(spuInfoEntity);

        // 描述信息
        if (request.getDetail() != null) {
            SpuDetailEntity spuDetail = new SpuDetailEntity();
            spuDetail.setId(spuInfoEntity.getId());
            spuDetail.setDetail(request.getDetail());
            spuDetailService.updateById(spuDetail);
        }

        // 图片信息
        List<String> images = request.getImages();
        spuImagesService.saveImages(spuInfoEntity.getId(),images);

        // 规格信息
        spuAttrValueService.saveBatchSpuBaseAttrs(request.getBaseAttrs(),spuInfoEntity.getId());

        if (Optional.ofNullable(request.getRegenerateSku()).orElse(false)) {
            this.saveSkuAndSkuAttr(request.getSkus(),spuInfoEntity);
            return;
        }

        // 更新 sku 信息
        request.getSkus().forEach(item -> {
            SkuEntity skuInfoEntity = new SkuEntity();
            BeanUtils.copyProperties(item,skuInfoEntity);
            skuService.updateById(skuInfoEntity);
        });

    }

    @Override
    public void deleteEsSku(List<Long> skuIds) {
        Result<Boolean> response = searchApi.deleteSku(skuIds);
        Assert.isTrue(response.isSuccess() && !response.getData(),() -> new BusinessException(ErrorCodeEnum.ES_DELETE_ERROR));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void spuSell(Long spuId) {
        List<SkuVO> skus =  skuService.listSkuBySpuId(spuId);
        // 可检索的基础属性
        List<SpuAttrValueEntity> baseAttr =
                spuAttrValueService.listBySpuId(spuId);
        List<Long> attrIds = baseAttr.stream().map(SpuAttrValueEntity::getAttrId).collect(Collectors.toList());
        List<Long> searchAttrs = attrService.filterSearchAttrIds(attrIds);
        Set<Long> searchAttrList = new HashSet<>(searchAttrs);
        List<SkuEsModelDTO.Attrs> searchBaseAttr = baseAttr.stream()
                .filter(item -> searchAttrList.contains(item.getAttrId()))
                .map(item -> {
                    SkuEsModelDTO.Attrs attrs = new SkuEsModelDTO.Attrs();
                    attrs.setAttrId(item.getId());
                    attrs.setAttrName(item.getAttrName());
                    attrs.setAttrValue(item.getAttrValue());
                    return attrs;
                })
                .collect(Collectors.toList());

        //TODO 发送远程调用 查看是否有库存
        //Map<Long , Boolean> skuMap = wareApi.hasStork(skuInfoEntities.stream().map(SkuInfoVO::getId).collect(Collectors.toList()));

        List<Long> brandIds = skus.stream().map(SkuVO::getBrandId).collect(Collectors.toList());
        List<Long> categoryIds = skus.stream().map(SkuVO::getCategoryId).collect(Collectors.toList());
        Map<Long, BrandEntity> brandMap = brandService.listByIds(brandIds).stream().collect(Collectors.toMap(BrandEntity::getId, Function.identity()));
        Map<Long, CategoryEntity> categoryMap = categoryService.listByIds(categoryIds).stream().collect(Collectors.toMap(CategoryEntity::getId, Function.identity()));

        List<SkuEsModelDTO> skuEsModel = skus.stream().map(sku -> {
            //组装信息
            SkuEsModelDTO to = new SkuEsModelDTO();
            BeanUtils.copyProperties(sku,to);
            to.setPrice(sku.getPrice());
            to.setCover(sku.getCover());

            //TODO 热度评分 0
            to.setHotScore(0L);

            //是否有库存
            //skuEsModelTO.setHasStock(skuMap.get("skuId"));

            BrandEntity brand = Optional.ofNullable(brandMap.get(sku.getBrandId())).orElse(new BrandEntity());
            CategoryEntity category = Optional.ofNullable(categoryMap.get(sku.getCategoryId())).orElse(new CategoryEntity());
            to.setBrandImg(brand.getLogo());
            to.setBrandName(brand.getName());
            to.setCategoryName(category.getName());
            to.setAttrs(searchBaseAttr);
            return to;
        }).collect(Collectors.toList());
        Result<?> r = searchApi.saveSku(skuEsModel);
        if (!r.isSuccess()) {
            throw new BusinessException(ErrorCodeEnum.ES_CONNECT_ERROR);
        }
        //改变商品的状态信息
        SpuEntity update = new SpuEntity();
        update.setId(spuId);
        update.setStatus(SpuEntity.SpuStatus.ON_SALE.getCode());
        this.updateById(update);
    }

    @Override
    public SpuEntity getSpuInfoBySkuId(Long skuId) {
        // 获取当前sku对应的spuId
        Long spuId = skuService.getSpuIdBySkuId(skuId);
        return this.getById(spuId);
    }

    @Override
    public SpuVO getSpuVoById(Long id) {
        return baseMapper.getVoById(id);
    }

    @Override
    public List<SpuSaleAttrVO> getSaleAttrBySpuId(Long spuId) {
        return baseMapper.getSaleAttrBySpuId(spuId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void soldOut(Long spuId) {
        List<Long> skuIds = skuService.listSkuBySpuId(spuId).stream().map(SkuVO::getId).collect(Collectors.toList());
        this.deleteEsSku(skuIds);
        this.update(Wrappers.<SpuEntity>lambdaUpdate().eq(SpuEntity::getId,spuId).set(SpuEntity::getStatus, SpuEntity.SpuStatus.SOLD_OUT.getCode()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSpu(long id) {
        // todo 验证是否有活动信息
        // 删除 spu_attr
        spuAttrValueService.deleteBySpuId(id);
        // delete sku_attr
        List<Long> skuIds = skuService.listSkuBySpuId(id).stream().map(SkuVO::getId).collect(Collectors.toList());
        if (!skuIds.isEmpty()) {
            skuAttrValueService.deleteBySkus(skuIds);
        }
        // delete sku
        skuService.deleteBySpuId(id);
        // delete spu_detail
        spuDetailService.removeById(id);
        // delete spu_image
        spuImagesService.deleteBySpuId(id);
        // todo delete esinfo
        this.deleteEsSku(skuIds);
        // delete spu
        this.removeById(id);
    }

    @Override
    public SpuViewVO spuView(Long skuId) {

        SpuViewVO spuDetailVO = new SpuViewVO();

        CompletableFuture<SpuEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            // spu基本信息
            SpuEntity info = this.getById(skuId);
            SpuDTO target = new SpuDTO();
            BeanUtils.copyProperties(info,target);
            spuDetailVO.setInfo(target);
            return info;
        },executor);

        CompletableFuture<Void> groupFuture = infoFuture.thenAcceptAsync(res -> {
            // spu分组属性获取
            List<SpuBaseAttrGroupVO> group = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getId(), res.getCategoryId());
            spuDetailVO.setGroupAttrs(group);
        },executor);

        CompletableFuture<Void> imageFuture = infoFuture.thenAcceptAsync(res -> {
            // spu分组属性获取
            List<SpuImagesVO> images = spuImagesService.listBySpuId(res.getId()).stream().map(item -> {
                SpuImagesVO target = new SpuImagesVO();
                BeanUtils.copyProperties(item,target);
                return target;
            }).collect(Collectors.toList());
            spuDetailVO.setImages(images);
        },executor);

        CompletableFuture<Void> saleFuture = infoFuture.thenAcceptAsync(res -> {
            // todo 放到spu里面去获取 sku销售属性获取
            List<SkuItemSaleAttrVO> sales = skuAttrValueService.getSaleAttrsBySpuId(res.getId());
            spuDetailVO.setSpuSaleAttrs(sales);
        },executor);

        CompletableFuture<Void> detailFuture = infoFuture.thenAcceptAsync(res -> {
            // todo 放到spu里面去获取 sku销售属性获取
            SpuInfoDetailVO detail = spuDetailService.getDescBySpuId(res.getId());
            spuDetailVO.setDesc(detail);
        },executor);

        //等待所有执行完毕后返回
        try {
            CompletableFuture.allOf(infoFuture,detailFuture,imageFuture,groupFuture,saleFuture).get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("{}",e);
            throw new RuntimeException(e);
        }

        return spuDetailVO;
    }

}